#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import logging

logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger("dabble")
logger.addHandler(logging.FileHandler("dabble.log", mode="a+"))

from flask import Flask, jsonify, request, abort
from flask.views import MethodView

from model.utils import *
from model.scrabble import Player, Game, WordFactory
from model.spellchecker import SpellChecker
import uuid
import json

if "-d" in sys.argv:
    from persistence.db import MockPlayerRepository, MockGameRepository
else:
    from persistence.db import MongoPlayerRepository, MongoGameRepository


spellchecker = SpellChecker("dicts/norwegian_words_washed.txt")
# Using pymongo (MongoDB)
mongodb_uri = os.environ.get("MONGOHQ_URL", "mongodb://localhost/scrabble")

if "-d" in sys.argv:
    # Or use a "mock" database
    logger.debug("Running in debug mode, thus using the mocked repository!")
    player_repository = MockPlayerRepository()
    game_repository = MockGameRepository()
else:
    player_repository = MongoPlayerRepository(uri=mongodb_uri)
    game_repository = MongoGameRepository(uri=mongodb_uri)

app = Flask(__name__)
app.debug = True

API_ROOT = "api"
API_VERSION = "v1"


def get_user(user_id):
    try:
        return player_repository.read(user_id)
    except:
        return None


def jsonify_player(player, games=[]):
    return jsonify({"user_id": player.uuid, "name": player.name,
                    "games": games})


def jsonify_game(game, user, opponent):
    return jsonify(gameid=game.uuid,
                   user_letters=game.get_user_letters(user.uuid),
                   opponent_letters=game.get_user_letters(opponent.uuid),
                   board=game.board.get_letters(),
                   user=user.uuid,
                   opponent=opponent.uuid,
                   active_player=game.get_active_player().uuid,
                   user_score=game.get_score(user),
                   opponent_score=game.get_score(opponent))


def create_api_path(resource):
    """
    Constructs an appropriate URL for an REST resource

    >>> create_api_path('users')
    /api/v1/users/
    """
    return "/%s/%s/%s/" % (API_ROOT, API_VERSION, resource)


class UserAPI(MethodView):
    def get(self, user_id=None):
        # This if-block is ONLY for debugging,
        # should not exist in production release!
        if not user_id:
            players = player_repository.read_all()

            users = [
                {"user_id": player.uuid, "name": player.name, "mail": player.mail}
                for player in players
            ]

            return jsonify(users=users)

        player = get_user(user_id)
        if not player:
            abort(400)

        games = game_repository.read_all_for_player(player.uuid)
        return jsonify_player(player, [g.uuid for g in games])

    def post(self):
        # create a new user
        name = request.form['name']
        mail = request.form['mail']
        if not name:
            logger.debug("Missing name")
            abort(400)
        if not mail:
            logger.debug("Missing mail")
            abort(400)

        player = Player(str(uuid.uuid4()), name, mail)
        try:
            player_repository.create(player)
        except Exception:
            abort(401)  # http code for 'user already exist'??

        return jsonify_player(player)


user_view = UserAPI.as_view('user_api')
user_path = create_api_path("users")
app.add_url_rule(user_path, view_func=user_view, methods=['POST', 'GET'])
app.add_url_rule(user_path + '<string:user_id>', view_func=user_view,
                 methods=['GET', 'PUT', 'DELETE'])


class GameAPI(MethodView):
    def get(self, game_id):
        if not game_id:
            games = game_repository.read_all()
            return jsonify(status='ok', games=[g.uuid for g in games])

        game = game_repository.read(game_id)
        user = game.players[0]
        opponent = game.players[1]

        return jsonify_game(game, user, opponent)

    def post(self):
        # Creating new game
        user, opponent = get_players_from_request(request)
        if not opponent and not user:
            abort(400)  # Not found

        logger.debug("Creating user and opponent letters")
        user_letters = create_rand_letters_object()
        opponent_letters = create_rand_letters_object()
        players_letters = [user_letters, opponent_letters]

        logger.debug("Creating game object")
        game = Game([user, opponent], str(uuid.uuid4()), players_letters)
        logger.debug("Add game object to repository")
        game_repository.create(game)

        return jsonify_game(game, user, opponent)

    def put(self, game_id):
        logger.debug("PUT game api called!")
        logger.debug("request: %s %s" % (request, game_id))

        if not game_id:
            logger.debug("Missing game id PUT request.")
            abort(400)

        game = game_repository.read(game_id)
        if not game:
            logger.warning("Game with id %s not found in games dict!" %
                game_id)
            abort(400)

        data = json.loads(request.data)
        user, opponent = get_players_from_dict(data)
        if not opponent and not user:
            abort(400)  # Not found

        try:
            request_letters = data['letters']
        except:
            logger.warning(
                "Missing input word for game %s by user %s and opponent %s" %
                           (game_id, user.uuid, opponent.uuid))
            abort(404)

        word = WordFactory.create_word(request_letters)
        game.set_word(user, word)

        return jsonify_game(game, user, opponent)


game_view = GameAPI.as_view('game_api')
game_path = create_api_path("games")
app.add_url_rule(game_path, defaults={'game_id': None},
                 view_func=game_view, methods=['GET', ])
app.add_url_rule(game_path + '<string:game_id>', view_func=game_view,
                 methods=['GET', 'PUT', 'DELETE'])
app.add_url_rule(game_path + '/<string:game_id>', view_func=game_view,
                 methods=['GET'])
app.add_url_rule(game_path, view_func=game_view, methods=['POST', ])


def get_players_from_request(req):
    """
        Extract user_id and opponent_id, and return the
        User objects
    """
    if req.data:
        return get_players_from_dict(json.loads(req.data))
    else:
        return get_players_from_dict(req.form)


def get_players_from_dict(request_dict):
    """
    Extract user uuid and opponent uuid, then do a
    lookup on them. Then returns a tuple with their
    respective Player objects.
    """
    user_id = request_dict['user_id']
    opponent_id = request_dict['opponent_id']

    user = get_user(user_id)
    if not user:
        logger.warning("cant get user with id: %s" % user_id)

    opponent = get_user(opponent_id)
    if not opponent:
        logger.warning("cant get opponent with id: %s" % opponent_id)

    return (user, opponent)


@app.route('/')
def main():
    return """
<h1>This is the super duper awesome Dabble app!</h2>
<br /><br />
Powered by opensource software and a bunch of software developers!
"""


@app.route('/spell/<string:word>')
def spellme(word):
    logger.debug("Spellchecking word: %s" % word)

    if spellchecker.check_word(word.lower()):
        return jsonify(status="ok", txt="Word is a norwegian word")
    else:
        return jsonify(status="failed", txt="Word is not a norwegian word")

if __name__ == "__main__":
    if "-d" in sys.argv:
        logger.setLevel(logging.DEBUG)

    # Bind to PORT if defined, otherwise default to 5000.
    port = int(os.environ.get('PORT', 5000))
    logger.info("Starting up dabble backend server.")
    app.run(host='0.0.0.0', port=port, debug=True)
    logger.info("Stopping dabble backend server.")
